package impl

import (
	"context"
	"fmt"
	"time"

	"gitee.com/go-course/go8/projects/devcloud/mcenter/apps/token"
	"gitee.com/go-course/go8/projects/devcloud/mcenter/apps/token/provider"
	"github.com/infraboard/mcube/exception"
)

func (s *service) IssueToken(ctx context.Context, req *token.IssueTokenRequest) (
	*token.Token, error) {
	// 颁发令牌
	tk, err := s.IssueTokenNow(ctx, req)
	if err != nil {
		return nil, err
	}
	return tk, nil
}

func (s *service) IssueTokenNow(ctx context.Context, req *token.IssueTokenRequest) (*token.Token, error) {
	// 获取令牌颁发器
	issuer := provider.Get(req.GrantType)
	if issuer == nil {
		return nil, fmt.Errorf("%s issuer not found", token.GRANT_TYPE_PASSWORD)
	}
	tk, err := issuer.IssueToken(ctx, req)
	if err != nil {
		return nil, err
	}

	// 入库保存
	if !req.DryRun {
		if err := s.save(ctx, tk); err != nil {
			return nil, err
		}

		// 用户单点
		// 用户登录成功, 之前的登录Token统统失效(Web)
		// 需要找到 该用户可用Token
		req := token.NewQueryTokenRequest()
		req.SetPlatform(token.PLATFORM_WEB)
		req.Username = "admin"
		req.SetIsBlook(false)
		set, err := s.QueryToken(ctx, req)
		if err != nil {
			s.log.Errorf("query user token for block error, %s", err)
		}
		for i := range set.Items {
			item := set.Items[i]
			// 非当前颁发Token
			if item.AccessToken != tk.AccessToken {
				item.Status.IsBlock = true
				item.Status.BlockAt = time.Now().Unix()
				item.Status.BlockReason = "当前用户已经从其他地方登录"
				if err := s.update(ctx, item); err != nil {
					s.log.Errorf("block token %s error, %s", item.AccessToken, err)
				}
			}
		}
	}

	return tk, nil
}

// 撤销Token
func (s *service) RevolkToken(ctx context.Context, req *token.RevolkTokenRequest) (
	*token.Token, error) {
	// 查询Token
	tk, err := s.get(ctx, req.AccessToken)
	if err != nil {
		return nil, err
	}

	if tk.RefreshToken != req.RefreshToken {
		return nil, exception.NewBadRequest("refresh token not connrect")
	}

	if err := s.delete(ctx, tk); err != nil {
		return nil, err
	}
	return tk, nil
}

// 切换Token空间
func (s *service) ChangeNamespace(ctx context.Context, req *token.ChangeNamespaceRequest) (
	*token.Token, error) {
	return nil, nil
}

// 校验Token
// @cache   funcA =  funcB(funcA)  funcA()==> funcB(funcA)
func (s *service) ValidateToken(ctx context.Context, req *token.ValidateTokenRequest) (
	*token.Token, error) {
	// 先查询Redis 有没有缓存Token 有的话 就直接从Redis获取
	// 没有的话 就查询数据库
	// 获取多久? 数据一致性如何保证? 需不需要保证一致性
	// 当有变更的时候: Update/Delete的时候， 需要清除缓存
	// 怎么实现?

	// if tk := get(tk); tk != nil {
	// 	return tk
	// }

	// 查询Token
	tk, err := s.get(ctx, req.AccessToken)
	if err != nil {
		return nil, err
	}

	if tk.Status.IsBlock {
		return nil, fmt.Errorf("token is blocked")
	}

	if tk.IsAccessTokenExpired() {
		return nil, fmt.Errorf("token has expired")
	}

	return tk, nil
}

// 查询Token, 用于查询Token颁发记录, 也就是登陆日志
func (s *service) QueryToken(ctx context.Context, req *token.QueryTokenRequest) (
	*token.TokenSet, error) {
	r := newQueryRequest(req)

	s.log.Debugf("filter: %s", r.FindFilter())
	resp, err := s.col.Find(ctx, r.FindFilter(), r.FindOptions())

	if err != nil {
		return nil, exception.NewInternalServerError("find token error, error is %s", err)
	}

	set := token.NewTokenSet()
	// 循环, 读取数据

	for resp.Next(ctx) {
		ins := token.NewDefaultToken()
		if err := resp.Decode(ins); err != nil {
			return nil, exception.NewInternalServerError("decode user error, error is %s", err)
		}
		set.Add(ins)
	}

	// count
	count, err := s.col.CountDocuments(ctx, r.FindFilter())
	if err != nil {
		return nil, exception.NewInternalServerError("get user count error, error is %s", err)
	}
	set.Total = count
	return set, nil
}
